package com.atguigu.lease.admin.service.impl;

import com.atguigu.lease.admin.mapper.*;
import com.atguigu.lease.admin.service.*;
import com.atguigu.lease.model.bean.*;
import com.atguigu.lease.model.dto.ApartmentItemQueryDto;
import com.atguigu.lease.model.dto.ApartmentSubmitDto;
import com.atguigu.lease.model.enums.ItemType;
import com.atguigu.lease.model.vo.ApartmentDetailVo;
import com.atguigu.lease.model.vo.ApartmentItemVo;
import com.atguigu.lease.model.vo.FeeValueVo;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.stream.Collectors;

/**
* @author 1
* @description 针对表【apartment_info(公寓信息表)】的数据库操作Service实现
* @createDate 2025-09-22 17:18:51
*/
@Service
public class ApartmentInfoServiceImpl extends ServiceImpl<ApartmentInfoMapper, ApartmentInfo>
    implements ApartmentInfoService{

    @Autowired
    ApartmentFacilityMapper apartmentFacilityMapper;//公寓配套表的Mapper
    @Autowired
    ApartmentLabelMapper apartmentLabelMapper;
    @Autowired
    ApartmentLabelService apartmentLabelService;
    @Autowired
    ApartmentFeeValueMapper  apartmentFeeValueMapper;
    @Autowired
    ApartmentFeeValueService apartmentFeeValueService;
    @Autowired
    GraphInfoMapper graphInfoMapper;
    @Autowired
    GraphInfoService graphInfoService;

    @Autowired
    ProvinceInfoService provinceInfoService;
    @Autowired
    CityInfoService cityInfoService;
    @Autowired
    DistrictInfoService districtInfoService;
    @Autowired
    @Lazy
    RoomInfoService roomInfoService;
    @Override
    public Page<ApartmentItemVo> getPageItem(ApartmentItemQueryDto queryDto) {
        // MybatisPlus mapper自定义方法支持 传入page对象，如果配置了分页拦截器，mp会自动根据传入的page对象实现分页查询
        //1、创建page对象： 携带 分页要查询的页码 和每页记录数
        Page<ApartmentItemVo> page = new Page<>(queryDto.getCurrent(), queryDto.getSize());
        //2、创建分页拦截器 注册给mybatis： common中已经配置了！
        //3、自定义mapper方法： mp会自动使用page对象实现分页(分页拦截器生效！！)
        List<ApartmentItemVo> list =  baseMapper.selectPageItem(page , queryDto);
        //4、将查询到的数据列表 手动设置给分页对象
        page.setRecords(list);
        //5、返回分页数据
        return page;
    }
    //springboot中事务管理默认已经开启，加事务注解的方法会直接生效
    @Transactional(rollbackFor = Exception.class)//启用本地事务管理：如果出现异常全部回滚
    @Override
    public void saveOrUpdateApartmentSubmitDto(ApartmentSubmitDto apartmentSubmitDto) {
        //0、判断是新增还是更新： 更新会提交公寓id， 新增不会提交公寓id
        boolean isUpdate = apartmentSubmitDto.getId()!=null;//有id返回true，表示更新
        //1、先保存公寓数据： 得到公寓id
        //查询省市区名称：
        Long provinceId = apartmentSubmitDto.getProvinceId();
        Long cityId = apartmentSubmitDto.getCityId();
        Long districtId = apartmentSubmitDto.getDistrictId();
        ProvinceInfo provinceInfo = provinceInfoService.getById(provinceId);
        CityInfo cityInfo = cityInfoService.getById(cityId);
        DistrictInfo districtInfo = districtInfoService.getById(districtId);
        //设置省市区名称
        apartmentSubmitDto.setProvinceName(provinceInfo.getName());
        apartmentSubmitDto.setCityName(cityInfo.getName());
        apartmentSubmitDto.setDistrictName(districtInfo.getName());

        // 调用父类的 更新或新增的业务方法， 传入ApartmentInfo对象
        saveOrUpdate(apartmentSubmitDto);//新增或更新生成的sql mp使用 ServiceImpl接受的泛型来解析 生成sql
        //2、如果是更新：无法判断提交的公寓的关联数据 哪些是新增的 哪些是之前已有的 无法知道数据库表中哪些数据是需要删除的
        //获取 公寓id： 如果是更新，id是前端提交的，如果是新增，id是数据库雪花算法生成的
        Long apartmentId = apartmentSubmitDto.getId();
        if(isUpdate){
            //  更新之前 可以先删除公寓所有的关联数据 然后插入本次提交的
            deleteByApartmentId(apartmentId);
        }
        //如果更新前：已经删除了关联数据，直接插入本次提交的关联数据
        //如果是新增： 直接插入本次提交的关联数据
        //3、保存公寓的其他数据： 需要使用公寓id和公寓关联
        //3.1 保存公寓的配套数据
        if(!CollectionUtils.isEmpty(apartmentSubmitDto.getFacilityInfoIds())){
            apartmentSubmitDto.getFacilityInfoIds().forEach(facilityInfoId -> {
                ApartmentFacility apartmentFacility = new ApartmentFacility();
                apartmentFacility.setApartmentId(apartmentId);
                apartmentFacility.setFacilityId(facilityInfoId.longValue());
                apartmentFacilityMapper.insert(apartmentFacility);
            });
        }

        //3.2 保存公寓的标签数据: 批量保存
        if(!CollectionUtils.isEmpty(apartmentSubmitDto.getLabelIds())){
            List<ApartmentLabel> apartmentLabels = apartmentSubmitDto.getLabelIds()
                    .stream() //将list集合转为stream流
                    //转换流：将所有数据遍历 转为另一个类型的对象
                    .map(labelId -> {
                        ApartmentLabel apartmentLabel = new ApartmentLabel();
                        apartmentLabel.setApartmentId(apartmentId);
                        apartmentLabel.setLabelId(labelId.longValue());
                        return apartmentLabel;
                    })
                    .collect(Collectors.toList());//将转换流的结果 封装到 List集合中
            //调用业务类单表的批量保存方法
            apartmentLabelService.saveBatch(apartmentLabels);
        }

        //3.3 保存公寓的杂费值数据
        if(!CollectionUtils.isEmpty(apartmentSubmitDto.getFeeValueIds())){
            List<ApartmentFeeValue> apartmentFeeValues = apartmentSubmitDto.getFeeValueIds()
                    .stream()
                    .map(feeValueId -> {
                        ApartmentFeeValue apartmentFeeValue = new ApartmentFeeValue();
                        apartmentFeeValue.setApartmentId(apartmentId);
                        apartmentFeeValue.setFeeValueId(feeValueId.longValue());
                        return apartmentFeeValue;
                    })
                    .collect(Collectors.toList());
            apartmentFeeValueService.saveBatch(apartmentFeeValues);
        }


        //3.4 保存公寓的图片列表
        List<GraphInfo> graphInfos = apartmentSubmitDto.getGraphVoList();
        if(!CollectionUtils.isEmpty(graphInfos)){
            //为每张图片设置它的类型 和关联的公寓id
            graphInfos.forEach(graphInfo -> {
                //防止前端更新 会提交图片id给后端
                graphInfo.setId(null);//因为新增公寓/更新公寓 之前会删除公寓所有的图片，然后插入
                graphInfo.setItemType(ItemType.APPARTMENT.getCode());
                graphInfo.setItemId(apartmentId);
            });
            // insert into graph_info(name,url,item_id,item_type) values()
            graphInfoService.saveBatch(graphInfos);
        }

    }
    //不能使用事务注解： 1、方法私有 动态代理无法使用，2、同类调用成员方法 动态代理会失效
    private void deleteByApartmentId(Long apartmentId) {
        //2.1 删除公寓的配套数据
        apartmentFacilityMapper.delete(Wrappers.lambdaQuery(ApartmentFacility.class)
                .eq(ApartmentFacility::getApartmentId ,apartmentId ));
        //2.2 删除公寓的标签数据
        apartmentLabelMapper.delete(Wrappers.lambdaQuery(ApartmentLabel.class)
                .eq(ApartmentLabel::getApartmentId ,apartmentId ));
        //2.3 删除公寓的杂费值数据
        apartmentFeeValueMapper.delete(Wrappers.lambdaQuery(ApartmentFeeValue.class)
                .eq(ApartmentFeeValue::getApartmentId ,apartmentId ));
        //2.4 删除公寓的图片列表
        graphInfoMapper.delete(Wrappers.lambdaQuery(GraphInfo.class)
                // 因为图片表会存储 公寓 和房间的图片，为了避免 公寓和房间id重复 导致删除错误 需要添加图片类型的判断
                .eq(GraphInfo::getItemId , apartmentId)
                .eq(GraphInfo::getItemType , ItemType.APPARTMENT)
        );
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void removeApartmentInfoById(Long id) {
        //数据安全校验：
        // 只能删除没有房间的公寓
        long count = roomInfoService.count(Wrappers.lambdaQuery(RoomInfo.class)
                .eq(RoomInfo::getApartmentId, id)
        );
        if(count>0){
            throw new RuntimeException("公寓存在未删除的房间，删除失败！");
        }
        //1、删除公寓数据
        this.removeById(id);
        //2、删除公寓关联数据
        this.deleteByApartmentId(id);
    }
    //根据id查询公寓详情
    @Override
    public ApartmentDetailVo getDetailById(Integer id) {
        //创建vo对象，然后为vo查询他需要的数据 设置给他
        ApartmentDetailVo apartmentDetailVo = new ApartmentDetailVo();
//        1- apartment_info:公寓数据
        ApartmentInfo apartmentInfo = getById(id);
        // BeanUtils：spring框架提供的工具类 用来拷贝对象属性值
        //可以将apartmentInfo相同名称、相同类型的属性值设置给vo对象
        BeanUtils.copyProperties(apartmentInfo, apartmentDetailVo);//参数1：源，参数2：目标对象
//        2- 公寓关联数据： 多表联查 必须自定义sql
//          2.1- apartment_facility:公寓配套表 ， 需要根据配套id 关联 facility_info表查询 配套详情数据
        List<FacilityInfo> facilityInfos = apartmentFacilityMapper.selectFacilityInfosByApartmentId(id);
        apartmentDetailVo.setFacilityInfoList(facilityInfos);

//          2.2- apartment_label： 公寓标签表， 需要根据公寓的标签id列表 关联 label_info表 查询公寓的标签数据
        List<LabelInfo> labelInfos = apartmentLabelMapper.selectLabelInfosByApartmentId(id);
        apartmentDetailVo.setLabelInfoList(labelInfos);
//          2.3- graph_info： 公寓房间图片表， 需要根据公寓id和类型 查询图片列表
        //单表查询 不需要自定义sql
        List<GraphInfo> graphInfos = graphInfoMapper.selectList(Wrappers.lambdaQuery(GraphInfo.class)
                .eq(GraphInfo::getItemId , id)
                .eq(GraphInfo::getItemType , ItemType.APPARTMENT));
        apartmentDetailVo.setGraphVoList(graphInfos);
//          2.4- apartment_fee_value: 公寓杂费值表，需要根据公寓的 杂费值id 关联 fee_value表 查询公寓的杂费值， 但是前端还需要展示杂费值的键名称，需要根据杂费值的fee_key_id关联 fee_key表 查询杂费值的键名称
        List<FeeValueVo> feeValueVos = apartmentFeeValueMapper.selectFeeValueVosByApartmentId(id);
        apartmentDetailVo.setFeeValueVoList(feeValueVos);
        //返回设置了所有属性值的 vo对象
        return apartmentDetailVo;
    }
}




